import { createMemoryHistory as createHistory } from 'history';

import * as TestSequences from './TestSequences';

describe('a memory history', () => {
  describe('by default', () => {
    let history;
    beforeEach(() => {
      history = createHistory();
    });

    describe('listen', () => {
      it('does not immediately call listeners', done => {
        TestSequences.Listen(history, done);
      });
    });

    describe('the initial location', () => {
      it('has a key', done => {
        TestSequences.InitialLocationHasKey(history, done);
      });
    });

    describe('push a new path', () => {
      it('calls change listeners with the new location', done => {
        TestSequences.PushNewLocation(history, done);
      });
    });

    describe('push the same path', () => {
      it('calls change listeners with the new location', done => {
        TestSequences.PushSamePath(history, done);
      });
    });

    describe('push state', () => {
      it('calls change listeners with the new location', done => {
        TestSequences.PushState(history, done);
      });
    });

    describe('push with no pathname', () => {
      it('calls change listeners with the normalized location', done => {
        TestSequences.PushMissingPathname(history, done);
      });
    });

    describe('push with a relative pathname', () => {
      it('calls change listeners with the normalized location', done => {
        TestSequences.PushRelativePathname(history, done);
      });
    });

    describe('push with a unicode path string', () => {
      it('creates a location with decoded properties', done => {
        TestSequences.PushUnicodeLocation(history, done);
      });
    });

    describe('push with an encoded path string', () => {
      it('creates a location object with decoded pathname', done => {
        TestSequences.PushEncodedLocation(history, done);
      });
    });

    describe('push with an invalid path string (bad percent-encoding)', () => {
      it('throws an error', done => {
        TestSequences.PushInvalidPathname(history, done);
      });
    });

    describe('replace a new path', () => {
      it('calls change listeners with the new location', done => {
        TestSequences.ReplaceNewLocation(history, done);
      });
    });

    describe('replace the same path', () => {
      it('calls change listeners with the new location', done => {
        TestSequences.ReplaceSamePath(history, done);
      });
    });

    describe('replace state', () => {
      it('calls change listeners with the new location', done => {
        TestSequences.ReplaceState(history, done);
      });
    });

    describe('replace  with an invalid path string (bad percent-encoding)', () => {
      it('throws an error', done => {
        TestSequences.ReplaceInvalidPathname(history, done);
      });
    });

    describe('location created by encoded and unencoded pathname', () => {
      it('produces the same location.pathname', done => {
        TestSequences.LocationPathnameAlwaysDecoded(history, done);
      });
    });

    describe('location created with encoded/unencoded reserved characters', () => {
      it('produces different location objects', done => {
        TestSequences.EncodedReservedCharacters(history, done);
      });
    });

    describe('goBack', () => {
      it('calls change listeners with the previous location', done => {
        TestSequences.GoBack(history, done);
      });
    });

    describe('goForward', () => {
      it('calls change listeners with the next location', done => {
        TestSequences.GoForward(history, done);
      });
    });

    describe('block', () => {
      it('blocks all transitions', done => {
        TestSequences.BlockEverything(history, done);
      });
    });

    describe('block a POP without listening', () => {
      it('receives the next location and action as arguments', done => {
        TestSequences.BlockPopWithoutListening(history, done);
      });
    });
  });

  describe('that denies all transitions', () => {
    const getUserConfirmation = (_, callback) => callback(false);

    let history;
    beforeEach(() => {
      history = createHistory({
        getUserConfirmation
      });
    });

    describe('push', () => {
      it('does not update the location', done => {
        TestSequences.DenyPush(history, done);
      });
    });

    describe('goBack', () => {
      it('does not update the location', done => {
        TestSequences.DenyGoBack(history, done);
      });
    });

    describe('goForward', () => {
      it('does not update the location', done => {
        TestSequences.DenyGoForward(history, done);
      });
    });
  });

  describe('a transition hook', () => {
    const getUserConfirmation = (_, callback) => callback(true);

    let history;
    beforeEach(() => {
      history = createHistory({
        getUserConfirmation
      });
    });

    it('receives the next location and action as arguments', done => {
      TestSequences.TransitionHookArgs(history, done);
    });

    it('cancels the transition when it returns false', done => {
      TestSequences.ReturnFalseTransitionHook(history, done);
    });
  });
});
